package com.megvii.corelib.components;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * Created by binghezhouke on 14-1-16.
 */
public class VariableImageView extends View
{
    /**
     * No touch points down.
     */
    private static final int MODE_NOTHING = 0;

    /**
     * One touch point down, dragging an object.
     */
    private static final int MODE_DRAG = 1;

    /**
     * Two or more touch points down, stretching/rotating an object using the
     * first two touch points.
     */
    private static final int MODE_PINCH = 2;

    private static final long EVENT_SETTLE_TIME_INTERVAL = 20;

    private static final float MAX_MULTITOUCH_POS_JUMP_SIZE = 30.0f;

    private static final float MAX_MULTITOUCH_DIM_JUMP_SIZE = 40.0f;

    private static final float MIN_MULTITOUCH_SEPARATION = 30.0f;

    // 只支持两点操作，反正多了也没有用
    static int MAX_TOUCH_POINTS = 20;

    // ----------------------------------------------------------------------------------------------------------------------
    private static final float[] xVals = new float[MAX_TOUCH_POINTS];

    private static final float[] yVals = new float[MAX_TOUCH_POINTS];

    private static final float[] pressureVals = new float[MAX_TOUCH_POINTS];

    private static final int[] pointerIds = new int[MAX_TOUCH_POINTS];

    Paint paint = new Paint();

    private Bitmap mContent;// 被切图的bitmap

    private Paint mOverLayPaint;

    private float x = 0;

    private float y = 0;

    private float rotate = 0;
    // ----------------------------------------------------------------------------------------------------------------------

    private float mDrawScale = 1.0f;

    private float mStartScale = 1.0f;

    private Rect mOverlayRect, mTmpRect;

    private float CONTENT_RATIO = 0.8f;//宽站屏幕宽的比例

    /** The current touch point */

    private float IDCARD_RATIO = 1.0f;//高和宽的比例（更具宽来算高）

    private float initX, initY;

    /**
     * Current drag mode
     */
    private int mMode = MODE_NOTHING;

    /**
     * The previous touch point
     */

    private PointInfo mCurrPt = new PointInfo();

    private PointInfo mPrevPt = new PointInfo();

    /**
     * Fields extracted from mCurrPt
     */
    private float mCurrPtX, mCurrPtY, mCurrPtDiam, mCurrPtWidth, mCurrPtHeight, mCurrPtAng, mStartPtAng, mStartPtDiam;

    private long mSettleStartTime = 0;

    private long mSettleEndTime = 0;

    public VariableImageView(Context context)
    {
        super(context);
    }

    public VariableImageView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        mTmpRect = new Rect();
        mOverlayRect = new Rect();
        mOverLayPaint = new Paint();
        mOverLayPaint.setColor(Color.BLACK);
        mOverLayPaint.setAlpha(200);
        mOverLayPaint.setDither(true);
        mOverLayPaint.setFilterBitmap(true);// 如果该项设置为true，则图像在动画进行中会滤掉对Bitmap图像的优化操作
        // ，加快显示 * 速度，本设置项依赖于dither和xfermode的设置
    }

    public VariableImageView(Context context, AttributeSet attrs, int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);
    }

    /**
     * 恢复
     */
    public void recover()
    {
        x = initX;
        y = initY;
        rotate = 0;
        mDrawScale = 1.0f;
        mStartScale = 1.0f;
        invalidate();
    }

    public void setContent(Bitmap bitmap)
    {
        mContent = bitmap;
        invalidate();
    }

    /**
     * Extract fields from mCurrPt, respecting the update* fields of mCurrPt.
     * This just avoids code duplication. I hate that Java doesn't support
     * higher-order functions, tuples or multiple return values from functions.
     */
    private void extractCurrPtInfo()
    {
        mCurrPtX = mCurrPt.getX();
        mCurrPtY = mCurrPt.getY();
        mCurrPtDiam = Math.max(MIN_MULTITOUCH_SEPARATION * .71f, mCurrPt.getMultiTouchDiameter());
        mCurrPtWidth = Math.max(MIN_MULTITOUCH_SEPARATION, mCurrPt.getMultiTouchWidth());
        mCurrPtHeight = Math.max(MIN_MULTITOUCH_SEPARATION, mCurrPt.getMultiTouchHeight());
        mCurrPtAng = mCurrPt.getMultiTouchAngle();
    }

    /**
     * Start dragging/pinching, or reset drag/pinch to current point if
     * something goes out of range
     */
    private void anchorAtThisPositionAndScale()
    {
        mStartScale = mDrawScale;
        extractCurrPtInfo();
        mStartPtDiam = Math.max(MIN_MULTITOUCH_SEPARATION * .71f, mCurrPt.getMultiTouchDiameter());
        mStartPtAng = mCurrPt.getMultiTouchAngle();
    }

    /**
     * Drag/stretch/rotate the selected object using the current touch
     * position(s) relative to the anchor position(s).
     */
    private void performDragOrPinch()
    {
        extractCurrPtInfo();
        mDrawScale = mCurrPtDiam / mStartPtDiam * mStartScale;

        int overW = mOverlayRect.width();
        int overH = mOverlayRect.height();
        int over = overW > overH ? overH : overW;
        if (mContent != null)
        {
            int bW = mContent.getWidth();
            int bH = mContent.getHeight();
            int l = bW > bH ? bH : bW;

            float bScale = over / (l * 1.0f);
            if (mDrawScale < bScale)
            {
                mDrawScale = bScale;
            }
        }

        rotate += -(Math.toDegrees(-1 * mCurrPt.getMultiTouchAngle()) - Math
                .toDegrees(-1 * mPrevPt.getMultiTouchAngle()));
        invalidate();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        int pointerCount = event.getPointerCount();
        // if (mMode == MODE_NOTHING && pointerCount == 1)
        // Not handling initial single touch events, just pass them on
        // return false;
        int action = event.getAction();
        int histLen = event.getHistorySize() / pointerCount;
        for (int histIdx = 0; histIdx <= histLen; histIdx++)
        {
            // Read from history entries until histIdx == histLen, then read
            // from current event
            boolean processingHist = histIdx < histLen;
            if (pointerCount == 1)
            {
                // Use single-pointer methods -- these are needed as a special
                // case (for some weird reason) even if
                // multitouch is supported but there's only one touch point down
                // currently -- event.getX(0) etc. throw
                // an exception if there's only one point down.
                xVals[0] = processingHist ? event.getHistoricalX(histIdx) : event.getX();
                yVals[0] = processingHist ? event.getHistoricalY(histIdx) : event.getY();
                pressureVals[0] = processingHist ? event.getHistoricalPressure(histIdx) : event
                        .getPressure();
            }
            else
            {
                // Read x, y and pressure of each pointer
                int numPointers = Math.min(pointerCount, MAX_TOUCH_POINTS);
                for (int ptrIdx = 0; ptrIdx < numPointers; ptrIdx++)
                {
                    int ptrId = event.getPointerId(ptrIdx);
                    pointerIds[ptrIdx] = ptrId;
                    // N.B. if pointerCount == 1, then the following methods
                    // throw an array index out of range exception,
                    // and the code above is therefore required not just for
                    // Android 1.5/1.6 but also for when there is
                    // only one touch point on the screen -- pointlessly
                    // inconsistent :(
                    xVals[ptrIdx] = processingHist ? event.getHistoricalX(ptrIdx, histIdx) : event
                            .getX(ptrIdx);
                    yVals[ptrIdx] = processingHist ? event.getHistoricalY(ptrIdx, histIdx) : event
                            .getY(ptrIdx);
                    pressureVals[ptrIdx] = processingHist ? event
                            .getHistoricalPressure(ptrIdx, histIdx) : event.getPressure(ptrIdx);
                }
            }
            // Decode event
            decodeTouchEvent(pointerCount, xVals, yVals, pressureVals, pointerIds, //
					/* action = */processingHist ? MotionEvent.ACTION_MOVE : action, //
					/* down = */processingHist ? true : action != MotionEvent.ACTION_UP //
                            && (action & ((1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT) - 1)) != MotionEvent.ACTION_POINTER_UP //
                            && action != MotionEvent.ACTION_CANCEL, //
                    processingHist ? event.getHistoricalEventTime(histIdx) : event.getEventTime());
        }
        return true;
    }

    private void decodeTouchEvent(int pointerCount, float[] x, float[] y, float[] pressure,
            int[] pointerIds, int action, boolean down, long eventTime)
    {
        PointInfo tmp = mPrevPt;
        mPrevPt = mCurrPt;
        mCurrPt = tmp;
        // Overwrite old prev point
        mCurrPt.set(pointerCount, x, y, pressure, pointerIds, action, down, eventTime);
        multiTouchController();
    }

    /**
     * State-based controller for tracking switches between no-touch,
     * single-touch and multi-touch situations. Includes logic for cleaning up
     * the event stream, as events around touch up/down are noisy at least on
     * early Synaptics sensors.
     */
    private void multiTouchController()
    {
        switch (mMode)
        {
            case MODE_NOTHING:
                // Not doing anything currently
                if (mCurrPt.isDown())
                {
                    // Start a new single-point drag
                    // Started a new single-point drag
                    mMode = MODE_DRAG;
                    // Don't need any settling time if just placing one finger,
                    // there is no noise
                    mSettleStartTime = mSettleEndTime = mCurrPt.getEventTime();
                }
                break;

            case MODE_DRAG:
                // Currently in a single-point drag
                if (!mCurrPt.isDown())
                {
                    // First finger was released, stop dragging
                    mMode = MODE_NOTHING;

                }
                else if (mCurrPt.isMultiTouch())
                {
                    // Point 1 was already down and point 2 was just placed down
                    mMode = MODE_PINCH;
                    // Restart the drag with the new drag position (that is at
                    // the midpoint between the touchpoints)
                    // Need to let events settle before moving things, to help
                    // with event noise on touchdown
                    mSettleStartTime = mCurrPt.getEventTime();
                    mSettleEndTime = mSettleStartTime + EVENT_SETTLE_TIME_INTERVAL;
                    anchorAtThisPositionAndScale();

                }
                else
                {
                    // Point 1 is still down and point 2 did not change state,
                    // just do single-point drag to new location
                    if (mCurrPt.getEventTime() < mSettleEndTime)
                    {
                        // Ignore the first few events if we just stopped
                        // stretching, because if finger 2 was kept down while
                        // finger 1 is lifted, then point 1 gets mapped to
                        // finger 2. Restart the drag from the new position.
                        // anchorAtThisPositionAndScale();
                    }
                    else
                    {
                        // Keep dragging, move to new point
                        // performDragOrPinch();
                        x += mCurrPt.xMid - mPrevPt.xMid;
                        y += mCurrPt.yMid - mPrevPt.yMid;
                        invalidate();
                    }
                }
                break;

            case MODE_PINCH:
                // Two-point pinch-scale/rotate/translate
                if (!mCurrPt.isMultiTouch() || !mCurrPt.isDown())
                {
                    // Dropped one or both points, stop stretching

                    if (!mCurrPt.isDown())
                    {
                        // Dropped both points, go back to doing nothing
                        mMode = MODE_NOTHING;
                    }
                    else
                    {
                        // Just dropped point 2, downgrade to a single-point
                        // drag
                        mMode = MODE_DRAG;
                        // Restart the pinch with the single-finger position
                        anchorAtThisPositionAndScale();
                        // Ignore the first few events after the drop, in case
                        // we dropped finger 1 and left finger 2 down
                        mSettleStartTime = mCurrPt.getEventTime();
                        mSettleEndTime = mSettleStartTime + EVENT_SETTLE_TIME_INTERVAL;
                    }

                }
                else
                {
                    // Still pinching
                    if (Math.abs(
                            mCurrPt.getX() - mPrevPt.getX()) > MAX_MULTITOUCH_POS_JUMP_SIZE || Math
                            .abs(mCurrPt.getY() - mPrevPt
                                    .getY()) > MAX_MULTITOUCH_POS_JUMP_SIZE || Math
                            .abs(mCurrPt.getMultiTouchWidth() - mPrevPt
                                    .getMultiTouchWidth()) * .5f > MAX_MULTITOUCH_DIM_JUMP_SIZE || Math
                            .abs(mCurrPt.getMultiTouchHeight() - mPrevPt
                                    .getMultiTouchHeight()) * .5f > MAX_MULTITOUCH_DIM_JUMP_SIZE)
                    {
                        // Jumped too far, probably event noise, reset and
                        // ignore events for a bit
                        anchorAtThisPositionAndScale();
                        mSettleStartTime = mCurrPt.getEventTime();
                        mSettleEndTime = mSettleStartTime + EVENT_SETTLE_TIME_INTERVAL;

                    }
                    else if (mCurrPt.eventTime < mSettleEndTime)
                    {
                        // Events have not yet settled, reset
                        // anchorAtThisPositionAndScale();
                    }
                    else
                    {
                        // Stretch to new position and size
                        performDragOrPinch();
                    }
                }
                break;
        }
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
        paint.setDither(true);// 设定是否使用图像抖动处理，会使绘制出来的图片颜色更加平滑和饱满，图像更加清晰
        paint.setAntiAlias(true);// 抗锯齿
        canvas.save();
        canvas.translate(x, y);
        canvas.rotate(rotate, 0, 0);
        canvas.scale(mDrawScale, mDrawScale);

        if (mContent != null)
        {
            canvas.drawBitmap(mContent, -mContent.getWidth() / 2, -mContent.getHeight() / 2, paint);
        }
        canvas.restore();
        drawViewfinder(canvas);
        //		if (mContent == null) {
        //			mOverLayPaint.setStrokeWidth(1);
        //			mOverLayPaint.setColor(0xff000000);
        //			mOverLayPaint.setTextSize(150);
        //			canvas.drawText("请读取图片", x - 350, y, mOverLayPaint);
        //		}
    }

    // 算出周围矩形的Rect
    @Override
    protected void onSizeChanged(int width, int height, int oldw, int oldh)
    {
        super.onSizeChanged(width, height, oldw, oldh);
        int centerX = width >> 1;
        int centerY = height / 3;
        int content_width = 0;
        int content_height = 0;
        if (width / (float) (height) < IDCARD_RATIO) // the viewHolder is to high
        {
            content_width = (int) (width * CONTENT_RATIO);
            content_height = (int) (content_width / IDCARD_RATIO);
        }
        else
        { // the viewHolder is too wide
            content_height = (int) (height * CONTENT_RATIO);
            content_width = (int) (content_height * IDCARD_RATIO);
        }

        mOverlayRect.left = centerX - content_width / 2;
        mOverlayRect.top = centerY - content_height / 2;
        mOverlayRect.right = centerX + content_width / 2;
        mOverlayRect.bottom = centerY + content_height / 2;
        initX = x = mOverlayRect.centerX();
        initY = y = mOverlayRect.centerY();
    }

    private void drawViewfinder(Canvas canvas)
    {

        mOverLayPaint.setStyle(Paint.Style.FILL);
        mOverLayPaint.setStrokeWidth(10);
        mOverLayPaint.setColor(0x80000000);

        // top
        mTmpRect.set(0, 0, getWidth(), mOverlayRect.top);
        canvas.drawRect(mTmpRect, mOverLayPaint);
        // bottom
        mTmpRect.set(0, mOverlayRect.bottom, getWidth(), getHeight());
        canvas.drawRect(mTmpRect, mOverLayPaint);
        // left
        mTmpRect.set(0, mOverlayRect.top, mOverlayRect.left, mOverlayRect.bottom);
        canvas.drawRect(mTmpRect, mOverLayPaint);
        // right
        mTmpRect.set(mOverlayRect.right, mOverlayRect.top, getWidth(), mOverlayRect.bottom);
        canvas.drawRect(mTmpRect, mOverLayPaint);

        // rect
        //mOverLayPaint.setStyle(Paint.Style.STROKE);
        mOverLayPaint.setColor(0xffffffff);
        mOverLayPaint.setStrokeWidth(1);
        int length = mOverlayRect.height();
        // left top
        canvas.drawLine(mOverlayRect.left, mOverlayRect.top, mOverlayRect.left + length,
                mOverlayRect.top, mOverLayPaint);
        canvas.drawLine(mOverlayRect.left, mOverlayRect.top, mOverlayRect.left,
                mOverlayRect.top + length, mOverLayPaint);

        // right top
        canvas.drawLine(mOverlayRect.right, mOverlayRect.top, mOverlayRect.right - length,
                mOverlayRect.top, mOverLayPaint);
        canvas.drawLine(mOverlayRect.right, mOverlayRect.top, mOverlayRect.right,
                mOverlayRect.top + length, mOverLayPaint);

        // left bottom
        canvas.drawLine(mOverlayRect.left, mOverlayRect.bottom, mOverlayRect.left + length,
                mOverlayRect.bottom, mOverLayPaint);
        canvas.drawLine(mOverlayRect.left, mOverlayRect.bottom, mOverlayRect.left,
                mOverlayRect.bottom - length, mOverLayPaint);

        // right bottom
        canvas.drawLine(mOverlayRect.right, mOverlayRect.bottom, mOverlayRect.right - length,
                mOverlayRect.bottom, mOverLayPaint);
        canvas.drawLine(mOverlayRect.right, mOverlayRect.bottom, mOverlayRect.right,
                mOverlayRect.bottom - length, mOverLayPaint);


    }

    public Bitmap getDrawBitmap()
    {// 截取出图来
        Bitmap tmp = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        draw(new Canvas(tmp));// 把这个view画到创建出的bitmap上
        // return Bitmap.createBitmap(tmp, mOverlayRect.left, mOverlayRect.top,
        // mOverlayRect.width(), mOverlayRect.height());
        return Bitmap.createBitmap(tmp, mOverlayRect.left + 2, mOverlayRect.top + 2,
                mOverlayRect.width() - 4, mOverlayRect.height() - 4);
    }

    /**
     * A class that packages up all MotionEvent information with all derived
     * multitouch information (if available)
     */
    public static class PointInfo
    {
        // Multitouch information
        private int numPoints;

        private float[] xs = new float[MAX_TOUCH_POINTS];

        private float[] ys = new float[MAX_TOUCH_POINTS];

        private float[] pressures = new float[MAX_TOUCH_POINTS];

        private int[] pointerIds = new int[MAX_TOUCH_POINTS];

        // Midpoint of pinch operations
        private float xMid, yMid, pressureMid;

        // Width/diameter/angle of pinch operations
        private float dx, dy, diameter, diameterSq, angle;

        // Whether or not there is at least one finger down (isDown) and/or at
        // least two fingers down (isMultiTouch)
        private boolean isDown, isMultiTouch;

        // Whether or not these fields have already been calculated, for caching
        // purposes
        private boolean diameterSqIsCalculated, diameterIsCalculated, angleIsCalculated;

        // Event action code and event time
        private int action;

        private long eventTime;

        // -------------------------------------------------------------------------------------------------------------------------------------------

        /**
         * Set all point info
         */
        private void set(int numPoints, float[] x, float[] y, float[] pressure, int[] pointerIds,
                int action, boolean isDown, long eventTime)
        {
            this.eventTime = eventTime;
            this.action = action;
            this.numPoints = numPoints;
            for (int i = 0; i < numPoints; i++)
            {
                this.xs[i] = x[i];
                this.ys[i] = y[i];
                this.pressures[i] = pressure[i];
                this.pointerIds[i] = pointerIds[i];
            }
            this.isDown = isDown;
            this.isMultiTouch = numPoints >= 2;

            if (isMultiTouch)
            {
                xMid = (x[0] + x[1]) * .5f;
                yMid = (y[0] + y[1]) * .5f;
                pressureMid = (pressure[0] + pressure[1]) * .5f;
                dx = Math.abs(x[1] - x[0]);
                dy = Math.abs(y[1] - y[0]);

            }
            else
            {
                // Single-touch event
                xMid = x[0];
                yMid = y[0];
                pressureMid = pressure[0];
                dx = dy = 0.0f;
            }
            // Need to re-calculate the expensive params if they're needed
            diameterSqIsCalculated = diameterIsCalculated = angleIsCalculated = false;
        }

        /**
         * Copy all fields from one PointInfo class to another. PointInfo
         * objects are volatile so you should use this if you want to keep track
         * of the last touch event in your own code.
         */
        public void set(PointInfo other)
        {
            this.numPoints = other.numPoints;
            for (int i = 0; i < numPoints; i++)
            {
                this.xs[i] = other.xs[i];
                this.ys[i] = other.ys[i];
                this.pressures[i] = other.pressures[i];
                this.pointerIds[i] = other.pointerIds[i];
            }
            this.xMid = other.xMid;
            this.yMid = other.yMid;
            this.pressureMid = other.pressureMid;
            this.dx = other.dx;
            this.dy = other.dy;
            this.diameter = other.diameter;
            this.diameterSq = other.diameterSq;
            this.angle = other.angle;
            this.isDown = other.isDown;
            this.action = other.action;
            this.isMultiTouch = other.isMultiTouch;
            this.diameterIsCalculated = other.diameterIsCalculated;
            this.diameterSqIsCalculated = other.diameterSqIsCalculated;
            this.angleIsCalculated = other.angleIsCalculated;
            this.eventTime = other.eventTime;
        }

        // -------------------------------------------------------------------------------------------------------------------------------------------

        /**
         * True if number of touch points >= 2.
         */
        public boolean isMultiTouch()
        {
            return isMultiTouch;
        }

        /**
         * Difference between x coords of touchpoint 0 and 1.
         */
        public float getMultiTouchWidth()
        {
            return isMultiTouch ? dx : 0.0f;
        }

        /**
         * Difference between y coords of touchpoint 0 and 1.
         */
        public float getMultiTouchHeight()
        {
            return isMultiTouch ? dy : 0.0f;
        }

        /**
         * Fast integer sqrt, by Jim Ulery. Much faster than Math.sqrt() for
         * integers.
         */
        private int julery_isqrt(int val)
        {
            int temp, g = 0, b = 0x8000, bshft = 15;
            do
            {
                if (val >= (temp = (((g << 1) + b) << bshft--)))
                {
                    g += b;
                    val -= temp;
                }
            } while ((b >>= 1) > 0);
            return g;
        }

        /**
         * Calculate the squared diameter of the multitouch event, and cache it.
         * Use this if you don't need to perform the sqrt.
         */
        public float getMultiTouchDiameterSq()
        {
            if (!diameterSqIsCalculated)
            {
                diameterSq = (isMultiTouch ? dx * dx + dy * dy : 0.0f);
                diameterSqIsCalculated = true;
            }
            return diameterSq;
        }

        /**
         * Calculate the diameter of the multitouch event, and cache it. Uses
         * fast int sqrt but gives accuracy to 1/16px.
         */
        public float getMultiTouchDiameter()
        {
            if (!diameterIsCalculated)
            {
                if (!isMultiTouch)
                {
                    diameter = 0.0f;
                }
                else
                {
                    // Get 1/16 pixel's worth of subpixel accuracy, works on
                    // screens up to 2048x2048
                    // before we get overflow (at which point you can reduce or
                    // eliminate subpix
                    // accuracy, or use longs in julery_isqrt())
                    float diamSq = getMultiTouchDiameterSq();
                    diameter = (diamSq == 0.0f ? 0.0f : (float) julery_isqrt(
                            (int) (256 * diamSq)) / 16.0f);
                    // Make sure diameter is never less than dx or dy, for trig
                    // purposes
                    if (diameter < dx)
                    {
                        diameter = dx;
                    }
                    if (diameter < dy)
                    {
                        diameter = dy;
                    }
                }
                diameterIsCalculated = true;
            }
            return diameter;
        }

        /**
         * Calculate the angle of a multitouch event, and cache it. Actually
         * gives the smaller of the two angles between the x axis and the line
         * between the two touchpoints, so range is [0,Math.PI/2]. Uses
         * Math.atan2().
         */
        public float getMultiTouchAngle()
        {
            if (!angleIsCalculated)
            {
                if (!isMultiTouch)
                {
                    angle = 0.0f;
                }
                else
                {
                    angle = (float) Math.atan2(ys[1] - ys[0], xs[1] - xs[0]);
                }
                angleIsCalculated = true;
            }
            return angle;
        }

        // -------------------------------------------------------------------------------------------------------------------------------------------

        /**
         * Return the total number of touch points
         */
        public int getNumTouchPoints()
        {
            return numPoints;
        }

        /**
         * Return the X coord of the first touch point if there's only one, or
         * the midpoint between first and second touch points if two or more.
         */
        public float getX()
        {
            return xMid;
        }

        /**
         * Return the array of X coords -- only the first getNumTouchPoints() of
         * these is defined.
         */
        public float[] getXs()
        {
            return xs;
        }

        /**
         * Return the X coord of the first touch point if there's only one, or
         * the midpoint between first and second touch points if two or more.
         */
        public float getY()
        {
            return yMid;
        }

        /**
         * Return the array of Y coords -- only the first getNumTouchPoints() of
         * these is defined.
         */
        public float[] getYs()
        {
            return ys;
        }

        /**
         * Return the array of pointer ids -- only the first getNumTouchPoints()
         * of these is defined. These don't have to be all the numbers from 0 to
         * getNumTouchPoints()-1 inclusive, numbers can be skipped if a finger
         * is lifted and the touch sensor is capable of detecting that that
         * particular touch point is no longer down. Note that a lot of sensors
         * do not have this capability: when finger 1 is lifted up finger 2
         * becomes the new finger 1. However in theory these IDs can correct for
         * that. Convert back to indices using MotionEvent.findPointerIndex().
         */
        public int[] getPointerIds()
        {
            return pointerIds;
        }

        /**
         * Return the pressure the first touch point if there's only one, or the
         * average pressure of first and second touch points if two or more.
         */
        public float getPressure()
        {
            return pressureMid;
        }

        /**
         * Return the array of pressures -- only the first getNumTouchPoints()
         * of these is defined.
         */
        public float[] getPressures()
        {
            return pressures;
        }

        // -------------------------------------------------------------------------------------------------------------------------------------------

        public boolean isDown()
        {
            return isDown;
        }

        public int getAction()
        {
            return action;
        }

        public long getEventTime()
        {
            return eventTime;
        }
    }
}